#pragma once
#include <memory>
#include <iostream>
#include <fstream>
#include <string>
#include <cassert>
#include <ctime>
#include <sstream>
#include <error.h>
#include "util.hpp"

namespace CH
{
	// 基类
	class LogSink
	{
	public:
		using prt = std::shared_ptr<LogSink>;
		virtual ~LogSink() {}
		// 将从data开始len字节的数据输出到指定位置
		virtual void log(const char *data, size_t size) = 0;
	};

	// 落地方向： 标准输出
	class StdoutSink : public LogSink
	{
	public:
		// 将日志消息写到标准输出
		void log(const char *data, size_t size) override
		{
			// 不要直接使用std::cout << 进行输出，因为不一定是字符串需要按照长度输出
			std::cout.write(data, size);
		}
	};

	// 落地方向： 指定文件
	class FileSink : public LogSink
	{
	public:
		//// 构造时传入文件名，并打开文件，将操作句柄管理起来
		FileSink(const std::string &pathname)
			: _pathname(pathname)
		{
			// 创建目录，如果该文件的路径存在，则不会创建
			CH::Util::file::create_directory(Util::file::file_path(_pathname));
			// std::ios::binary:以二进制模式打开文件，用于处理二进制数据。
			// std::ios::app:以追加模式打开文件，新写入的数据会被添加到文件末尾。
			_ofs.open(_pathname, std::ios::binary | std::ios::app);//如果没有该文件，则会直接创建文件
			assert(_ofs.is_open());
		}

		// 将数据写入流中
		void log(const char *data, size_t size) override
		{
			_ofs.write(data, size);
			// 检查流的状态是否良好
			assert(_ofs.good());
		}

	private:
		std::ofstream _ofs;
		std::string _pathname; // 路径名
	};

	// 落地方向： 翻滚文件
	class RollSizeSink : public LogSink
	{
	public:
		RollSizeSink(const std::string &basename, size_t max_filesize)
			: _basename(basename), _max_filesize(max_filesize), _cur_filesize(0), _file_count(0)
		{
			std::string pathname = CreateNewFile();
			// 返回文件的目录，并且判断，如果目录不存在则创建
			Util::file::create_directory(Util::file::file_path(pathname));
			// 以追加的方式打开文件
			_ofs.open(pathname, std::ios::binary | std::ios::app);
			assert(_ofs.is_open());
		}

		// 将数据写入文件
		void log(const char *data, size_t size) override
		{
			// 如果文件满了，则重新创建文件
			if (_cur_filesize + size > _max_filesize)
			{
				// 关闭原来的文件
				_ofs.close();
				// 得到新文件的名字
				std::string pathname = CreateNewFile();
				_cur_filesize = 0;
				Util::file::create_directory(Util::file::file_path(pathname));
				_ofs.open(pathname, std::ios::binary | std::ios::app);
				assert(_ofs.is_open());
			}
			// 如果文件没有满，则直接写入
			_ofs.write(data, size);
			_cur_filesize+=size;
			assert(_ofs.good());
		}

	private:
		// 创建新文件名
		std::string CreateNewFile()
		{
			_file_count += 1;
			// 获取系统时间
			time_t now = Util::date::now();
			struct tm t;
			localtime_r(&now, &t);
			std::stringstream filename;
			filename << _basename;
			filename << (t.tm_year + 1900);
			filename << "-";
			filename << (t.tm_mon + 1);
			filename << "-";
			filename << t.tm_mday;
			filename << " ";
			filename << t.tm_hour;
			filename << ":";
			filename << t.tm_min;
			filename << ":";
			filename << t.tm_sec;
			filename << "-";
			filename << _file_count;
			filename << ".log";
			return filename.str();
		}

	private:
		std::string _basename; // 基础文件名 + 扩展文件名(以时间来生成) = 实际输出文件名/
		std::ofstream _ofs;	   // 操作句柄
		size_t _max_filesize;  // 记录文件允许存储最大数据量
		size_t _cur_filesize;  // 记录当前文件已经写入数据大小
		size_t _file_count;	   // 记录当前文件的数量
	};

	//落地工厂
	class SinkFactory
	{
	public:
		template <typename SinkType, typename... Args>
		static LogSink::prt create(Args &&...args)
		{
			return std::make_shared<SinkType>(std::forward<Args>(args)...);
		}
	};

}

